package visitors;

import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import nodes.Type;
import table.DataDeclaration;
import table.Declaration;
import table.FunctionDeclaration;
import table.SymbolTable;
import table.VariableDeclaration;

public class SymbolTablePrettyPrintVisitor {

    

    public String visit(VariableDeclaration vd)
    {
        return vd.getType().getType();
    }

    public String visit(FunctionDeclaration fd)
    {
        StringBuilder b = new StringBuilder();
        boolean first = true;
        for (Type field : fd.getParams())
        {
            if (first)
            {
                
                b.append(field.getType());
                first = false;
            }
            else
            {
                b.append(',').append(field.getType());
            }
        }
        b.append(':').append(fd.getReturnType().getType());
        return b.toString();
    }

    public String visit(DataDeclaration dd)
    {
        StringBuilder b = new StringBuilder();
        boolean first = true;
        for (Type field : dd.getFields())
        {
            if (first)
            {
                b.append(field.getType());
                first = false;
            }
            else
            {
                b.append(',').append(field.getType());
            }
        }
        return b.toString();
    }

    public void visit(SymbolTable table)
    {
        int[] lengths = maxLengths(table.getEntries().entrySet());
        System.out.println();
        printPath(table);
        System.out.println();
        printLine(lengths);
        printTitles(lengths);
        printLine(lengths);
        printEntries(lengths, table.getEntries());
        printLine(lengths);
        for (SymbolTable child : table.getChildren())
        {
            child.accept(this);
        }
    }

    private void printLine(int[] lengths)
    {
        System.out.print("+");
        for (int i = 0; i < lengths[0] ; i++) System.out.print("-");
        System.out.print("+--------+");
        for (int i = 0; i < lengths[1] ; i++) System.out.print("-");
        System.out.println("+");
    }

    private void printTitles(int[] lengths)
    {
        System.out.print("| Name ");
        for (int i = 6; i < lengths[0]; i++)
        {
            System.out.print(" ");
        }
        System.out.print("| Kind   | Type ");
        for (int i = 6; i < lengths[1]; i++)
        {
            System.out.print(" ");
        }
        System.out.println("|");
    }

    private void printPath(SymbolTable t)
    {
        if (t.getParent() != null)
        {
            printPath(t.getParent());
            System.out.print(" -> " + t.getNode().getClass().getSimpleName());
            return;
        }
        System.out.print(t.getNode().getClass().getSimpleName());
    }

    private void printEntries(int[] lengths, Map<String, List<Declaration>> table)
    {
        if (table.isEmpty())
        {
            System.out.println("|      no  entries     |");
            return;
        }
        Iterator<Map.Entry<String, List<Declaration>>> iterator = table.entrySet().iterator();
        while (iterator.hasNext())
        {
            Entry<String, List<Declaration>> e = iterator.next();
            String key = e.getKey();
            List<Declaration> values = e.getValue();
            for (Declaration d : values)
            {
                System.out.print("| " + key);
                for (int i = key.length() + 1; i < lengths[0]; i++)
                {
                    System.out.print(' ');
                }
                String value = (String) d.accept(this);
                int length = value.length();
                if (d.getClass() == DataDeclaration.class)
                {
                    System.out.print("| data   | ");
                }
                else if (d.getClass() == FunctionDeclaration.class)
                {
                    System.out.print("| method | ");
                }
                else if (d.getClass() == VariableDeclaration.class)
                {
                    System.out.print("| var    | ");
                }
                System.out.print(value);
                for (int i = 0; i < lengths[1]-length-1; i++)
                {
                    System.out.print(' ');
                }
                System.out.println('|');
            }

        }
    }

    private int[] maxLengths(Set<Map.Entry<String, List<Declaration>>> e)
    {
        int[] max = {0, 0};
        Iterator<Map.Entry<String, List<Declaration>>> i = e.iterator();
        while (i.hasNext())
        {
            Entry<String, List<Declaration>> cur = i.next();
            String key = (String) cur.getKey();
            max[0] = (key.length() > max[0]) ? key.length() : max[0];
            List<Declaration> values = cur.getValue();
            for (Declaration d : values)
            {
                Integer length = ((String) d.accept(this)).length();
                max[1] = (length > max[1]) ? length : max[1];
            }
        }
        max[0] += 2;
        max[1] += 2;
        max[0] = (max[0] < 6) ? 6 : max[0];
        max[1] = (max[1] < 6) ? 6 : max[1];
        return max;
    }
}